﻿
namespace TingSLsocket

open System
open System.IO
open Polenter.Serialization

[<AbstractClass>]
type JobAgent(_id : Guid) as this=
    let _cmdparser = new CommandParser()
    let _deserializer = new CommandSerializer()

    let _evtForward = new Event<Guid*byte[]>()
    let _evtError = new Event<Guid*exn>()

    let _mailbox = MailboxProcessor<int*obj>.Start(fun inbox ->
        let serializer = new CommandSerializer()
        let rec loop () = async {
            let! (reqcmd,reqparm) = inbox.Receive()

            let replycmd,replyparm = this.Handle(reqcmd,reqparm)
            if replycmd >= 0 then
                let replybuffer = serializer.Serialize(replycmd,replyparm)
                _evtForward.Trigger(_id,replybuffer)

            return! loop ()
        }
        loop ()
    )

    let onCmdParsedout (cmd,buffer,offset,size) =
        let parm = _deserializer.Deserialize(buffer,offset,size)
        _mailbox.Post(cmd,parm)

    do
        _cmdparser.CmdParsedEvt.Add onCmdParsedout
        _cmdparser.ParsingException.Add (fun evtarg->_evtError.Trigger(_id,evtarg.Argument))
        _cmdparser.Start()

        _mailbox.Error.Add (fun error -> _evtError.Trigger(_id,error))

    // if the return "cmd" < 0
    // it means no response
    abstract member Handle : int*obj -> int*obj

    abstract member Clean : unit->unit
    default this.Clean() =
        (_deserializer :> IDisposable).Dispose()
        _cmdparser.Close()
        (_mailbox :> IDisposable).Dispose()

    member this.Receive(buffer,offset,size) =
        _cmdparser.Write(buffer,offset,size)

    member this.Request (cmd,parm) =
        _mailbox.Post(cmd,parm)

    [<CLIEvent>]
    member this.ForwardEvt = _evtForward.Publish

    [<CLIEvent>]
    member this.ErrorEvt = _evtError.Publish

    interface IDisposable with
        member this.Dispose() =
            this.Clean()            

